using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;

// change this to your Plugin's namespace
using PluginInfo = StationeersServerPreference.PluginInfo;

namespace VisualmodBep;

public class Translation
{
    static Translation()
    {
        // Log.LogInfo($"Translation cctor {XmlPath}");
        _T = new Translation(); // must be called after XmlPath inited, so write in static ctor
    }

    public static readonly string DefaultLocale = "en";
    public static readonly string[] AcceptLocales = { "en", "zh" };

    // make sure path exists
    private static readonly string XmlPath = $"BepInEx/plugins/{PluginInfo.PLUGIN_NAME}/Localization";
    private static Translation _T;
    private Dictionary<string, TranslationItem> items = new Dictionary<string, TranslationItem>();

    private static string _L = "en";
    public static string Locale
    {
        get => _L; set
        {
            Log.LogInfo($"Translation Locale \"{value}\"");
            _L = value;
            foreach (TranslationItem v in _T.items.Values)
            {
                v.OnLocaleUpdate(_L);
            }
        }
    }

    public static string T(string id)
    {
        if (_T.items.ContainsKey(id))
        {
            return _T.items[id].str;
        }
        else
        {
            Log.LogError($"Translation T miss key \"{id}\"");
            return id;
        }
    }

    public static string T(string id, string fallback)
    {
        if (_T.items.ContainsKey(id))
        {
            return _T.items[id].str;
        }
        else
        {
            Log.LogInfo($"Translation T fallback key \"{id}\"");
            return fallback;
        }
    }

    public static string[] T(string[] idArray, string ns, string fallback = null)
    {
        return idArray.Select(id =>
        {
            if (_T.items.ContainsKey(ns + id))
            {
                return _T.items[ns + id].str;
            }
            else if (fallback != null)
            {
                return fallback;
            }
            else
            {
                Log.LogError($"Translation T miss key \"{ns + id}\"");
                return id;
            }
        }).ToArray();
    }

    public static bool Contains(string id)
    {
        return _T.items.ContainsKey(id);
    }

    public Translation()
    {
        RegisterFromXml();
    }

    public void Register(string id, params string[] transKV)
    {
        items[id] = new TranslationItem(id, transKV);
    }

    private int RegisterFromXml()
    {
        int xmlCount = 0;
        Log.LogInfo("  Translation RegisterFromXml");
        List<LocalizationXml> xmls = Helper.Xml.LoadXml<LocalizationXml>(XmlPath);
        foreach (LocalizationXml xml in xmls)
        {
            // Log.LogInfo("  " + xml.Plugin);
            if (xml.Plugin != PluginInfo.PLUGIN_NAME)
            {
                continue;
            }
            foreach (LocalizationXml.Record record in xml.Records)
            {
                // Log.LogInfo($"    {record.key} ({record.translations.Length})" );
                TranslationItem item = new TranslationItem(record.key);
                foreach (XmlElement e in record.translations)
                {
                    // Log.LogInfo("    " + e.Name + ": " + e.InnerXml);
                    item.AddTranslation(e.Name, e.InnerText);
                }
                items[item.id] = item;
            }
            xmlCount++;
        }
        Log.LogInfo($"  Translation RegisterFromXml {items.Count} keys {xmlCount} files");
        return xmlCount;
    }
}

[XmlRoot(ElementName = "Localization")]
public class LocalizationXml
{
    [XmlElement]
    public string Plugin; // must be the same as PluginInfo.PLUGIN_NAME

    [XmlArray]
    public Record[] Records;


    public class Record
    {
        [XmlAttribute]
        public string key; // eg. `<Record key="text">`

        [XmlAnyElement]
        public object[] translations; // eg. `<en>text</en>`
    }
}

public class TranslationItem
{
    public string id { get; private set; }
    public string str { get; private set; }
    private Dictionary<string, string> translations = new Dictionary<string, string>();

    public TranslationItem(string id, params string[] transKV)
    {
        this.id = id;
        if (transKV == null || transKV.Length < 2)
        {
            // Log.LogError("TranslationItem ctor no translations");
            return;
        }
        this.str = transKV[1];
        for (int i = 0; i + 1 < transKV.Length; i += 2)
        {
            this.translations[transKV[i]] = transKV[i + 1];
        }
    }

    public void AddTranslation(string loc, string translation)
    {
        this.translations.Add(loc, translation);
    }

    public void OnLocaleUpdate(string loc)
    {
        if (translations.ContainsKey(loc))
        {
            this.str = translations[loc];
        }
        else if (translations.ContainsKey(Translation.DefaultLocale))
        {
            Log.LogInfo($"TranslationItem OnLocaleUpdate \"{id}\" miss locale \"{loc}\", fallback to \"{Translation.DefaultLocale}\"");
            this.str = translations[Translation.DefaultLocale];
        }
        else
        {
            Log.LogError($"TranslationItem OnLocaleUpdate \"{id}\" miss default transaltion");
            this.str = translations.Count() > 0 ? translations.First().Value : id;
        }
    }
}
